//
// Create by Zeng Yun on 2018/12/21
//
// 家庭重要组成部分，设备容器 Container
//

package model

const (
	UnitPercentage = "percentage"
	UnitArcDegrees = "arcdegrees"
	UnitCelsius    = "celsius"
	UnitLux        = "lux"
	UnitSeconds    = "seconds"
)

const (
	FormatString    = "string"
	FormatBool      = "bool"
	FormatFloat     = "float"
	FormatUInt8     = "uint8"
	FormatUInt16    = "uint16"
	FormatUInt32    = "uint32"
	FormatInt32     = "int32"
	FormatTLV8      = "tlv8"
	FormatAnyObject = "any"
)

const (
	HMCharacteristicTypeName                        = "1"
	HMCharacteristicTypeModel                       = "3"
	HMCharacteristicTypeManufacturer                = "2"
	HMCharacteristicTypeSerialNumber                = "4"
	HMCharacteristicTypeFirmwareRevision            = "5"
	HMCharacteristicTypeCurrentTemperature          = "11"
	HMCharacteristicTypeCurrentRelativeHumidity     = "12"
	HMCharacteristicTypeCurrentAmbientLightLevel    = "13"
	HMCharacteristicTypeCurrentAmbientInfraredLevel = "14"
	HMCharacteristicTypeContactSensorState          = "15"
	HMCharacteristicTypeOn                          = "16"
	HMCharacteristicTypeTargetTemperature           = "17"
	HMCharacteristicTypeAirConditionerMode          = "18"
	HMCharacteristicTypeAirConditionerRotationMode  = "19"
	HMCharacteristicTypeAirConditionerWindSpeed     = "1A"
	HMCharacteristicTypeScreenBrightness            = "1B"
	HMCharacteristicTypeCurrentTime                 = "1C"
	HMCharacteristicTypeVolume                      = "1D"
)

type HMCharacteristic struct {
	Id    int         `json:"cid" bson:"cid"`
	Type  string      `json:"type" bson:"type"`
	Value interface{} `json:"value,omitempty" bson:"value,omitempty"`

	Format string `json:"format" bson:"format"`
	Unit   string `json:"unit,omitempty" bson:"unit,omitempty"`

	MaxValue  interface{} `json:"max,omitempty" bson:"max,omitempty"`
	MinValue  interface{} `json:"min,omitempty" bson:"min,omitempty"`
	StepValue interface{} `json:"step,omitempty" bson:"step,omitempty"`
}

const (
	HMServiceTypeAccessoryInformation = "1"  // 配件基本属性
	HMServiceTypeAccessoryBridge      = "2"  // 桥接服务
	HMServiceTypeAccessoryDFU         = "3"  // 固件升级
	HMServiceTypeContactSensor        = "11" // 接触传感器: 门磁
	HMServiceTypeHTSensor             = "12" // 湿度计
	HMServiceTypeLightSensor          = "13" // 光线传感器
	HMServiceTypeOutlet               = "14" // 插座
	HMServiceTypeSwitch               = "15" // 开关
	HMServiceTypeAirConditioner       = "16" // 空调
	HMServiceTypeClock                = "17" // 闹钟
)

type HMService struct {
	ID              int                 `json:"sid" bson:"sid"`
	Type            string              `json:"type" bson:"type"`
	State           string              `json:"state,omitempty" bson:"state,omitempty"`
	Characteristics []*HMCharacteristic `json:"characteristics" bson:"characteristics"`
}

const (
	HMAccessoryTypeHomeMaster            = 1
	HMAccessoryTypeHomeMasterV2          = 2
	HMAccessoryTypeOutlet                = 3
	HMAccessoryTypeSwitch                = 4
	HMAccessoryTypeContactSensor         = 6
	HMAccessoryTypeHTSensor              = 7
	HMAccessoryTypeColorLight            = 8
	HMAccessoryTypeTemperatureColorLight = 9
	HMAccessoryTypeMotionSensor          = 10
)

type HMAccessory struct {
	UUID        string       `json:"aid" bson:"aid"`
	Type        int          `json:"type" bson:"type"`
	IsReachable bool         `json:"-" bson:"-"`
	Services    []*HMService `json:"services" bson:"services"`
}

func (a *HMAccessory) SetReachable(reachable bool) {
	a.IsReachable = reachable
}

type Container struct {
	Id          string         `json:"id" bson:"id"`
	Version     int            `json:"version" bson:"version"`
	Reachable   bool           `json:"reachable" bson:"-"`
	Accessories []*HMAccessory `json:"accessories" bson:"accessories"`
}

func (c *Container) findAccessoryById(id string) *HMAccessory {
	for _, a := range c.Accessories {
		if a.UUID == id {
			return a
		}
	}
	return nil
}

// 获取属性
func (c *Container) getCharacteristic() []*Characteristic {
	if c.Reachable == false {
		return nil
	}
	var cchars []*Characteristic
	for _, a := range c.Accessories {
		if a.IsReachable == true {
			for _, s := range a.Services {
				for _, c := range s.Characteristics {
					switch c.Type {
					case HMCharacteristicTypeName,
						HMCharacteristicTypeModel,
						HMCharacteristicTypeManufacturer,
						HMCharacteristicTypeSerialNumber:
						continue
					}
					char := &Characteristic{
						AId:   a.UUID,
						SId:   s.ID,
						CId:   c.Id,
						Value: c.Value,
					}
					cchars = append(cchars, char)
				}
			}
		}
	}
	return cchars
}

// 获取属性类型
func (c *Container) GetCharacteristicType(char *Characteristic) string {
	for _, a := range c.Accessories {
		if a.UUID == char.AId {
			for _, s := range a.Services {
				if s.ID == char.SId {
					for _, c := range s.Characteristics {
						if c.Id == char.CId {
							return c.Type
						}
					}
				}
			}
		}
	}
	return ""
}

// 更新属性
func (c *Container) RefreshByCharacteristics(chars ...*Characteristic) error {
	c.Reachable = true
	for _, a := range c.Accessories {
		a.IsReachable = false
	}
	for _, char := range chars {
		if a := c.findAccessoryById(char.AId); a != nil {
			a.IsReachable = true
			for _, s := range a.Services {
				if s.ID == char.SId {
					for _, c := range s.Characteristics {
						if c.Id == char.CId {
							c.Value = char.Value
						}
					}
				}
			}
		}
	}
	return nil
}

// 更新容器属性
func (c *Container) UpdateCharacteristics(chars ...*Characteristic) error {
	c.Reachable = true
	for _, char := range chars {
		if a := c.findAccessoryById(char.AId); a != nil {
			a.IsReachable = true
			for _, s := range a.Services {
				if s.ID == char.SId {
					for _, c := range s.Characteristics {
						if c.Id == char.CId {
							c.Value = char.Value
						}
					}
				}
			}
		}
	}
	return nil
}

// 获取容器属性
func (c *Container) GetContainerCharacteristic() *ContainerCharacteristic {
	data := ContainerCharacteristic{
		Id:          c.Id,
		Version:     c.Version,
		IsReachable: c.Reachable,
	}
	if cchars := c.getCharacteristic(); cchars != nil {
		data.Chars = cchars
	}
	return &data
}
